/*Author:S.Genikhov
Creatin Date:
Last modified:11/Sept/2010
Reviewed by :

Version 3 with possiblitiest to : Login,get,Bye and simple Security check.
Support only TCP connection.

Short project description:
Bases reallisation of FTP server command like : cd ,send recieve file and commands,
by using of state machine*/

#include    <stdio.h>
#include    <errno.h>
#include    <string.h>
#include    <assert.h>
#include    <unistd.h>
#include    <stdlib.h>

#include "internalftpclient.h"

 FTPHead   HeadNULL;

/*******************FTPClientFuncions*****************************************************/

/*This is like callback function supplet to generic client*/
int FPTClientSession(int _socketID, struct sockaddr_in* addr);
/******************************************************************************************/


int main()
{

    Gcl* cl = GclNewClient(TCP,FPTClientSession,TCP_PORT,IP);
    if (-1 != GclConnectClient(cl))
    {
        GclRunClient(cl);
    }

    return 0;
}


int FPTClientSession(int _socketID, struct sockaddr_in* addr)
{
    char      headbuffer[MAXPACKAGESIZE]={0};/*declared in genreclient.h*/
    char      recievbuf[MAXPACKAGESIZE]={0};
    char      command[MAXPACKAGESIZE]={0};
    char      param[MAXPROMTLENGH]={0};
    int       ln;
    int       wr;
    int       index;


    while(1)
    {
        /*Get and analyse user promt*/
        do
        {
            while ( FTPGlientGetUserPromt(OUT command,OUT param) )
            {
                puts("Wrong Input\n");
            }

        } while ( (index = FTPFinedInListOfStr(command,(char*) FTPevents,FTP_NUMEVENT_E,COMMANDLENGTH)) == -1);


        if (index == FTP_Bye_E)
        {
            return 0;
        }


        /*Fill header*/
        FTPFillHeadr(headbuffer,0,LAST_P,0,COMMAND_C);
        /*Add header to buffer*/
        ln = FTPClientAddHeader(headbuffer,command,param);
#ifdef unitest_ftp_client
    fprintf(stdout," %s\n",headbuffer+sizeof(FTPHead));
#endif /*unitest_ftp_client*/

        /*send message*/
        wr= send(_socketID,headbuffer,ln,0);/*will be  blocked untill message not sent*/

        if (wr != ln)
        {
            perror("send :");
            fprintf(stderr,"\n Wrong socket work\n");
        }

        if(wr == -1 && errno == EPIPE)
        {
            perror("send :");
            fprintf(stderr,"\n Disconnect\n");
           return wr;
        }

/*TODO*/if (wr != ln)/*Unexpected stat need to be investigated*/
        {

            continue;
        }

        while( 1 )
        {
            wr = recv(_socketID,recievbuf,MAXPACKAGESIZE,0);/*will be  blocked untill message not recieved*/
            if(wr == -1 && errno == EPIPE)
            {
                perror("recv :");
                fprintf(stderr,"\n Disconnect\n");
                return wr;
            }

            FTPclientConvertHeader((FTPHead*)recievbuf);/*Convert Header to ntohl*/


            if ( FTPClientIsFile((FTPHead*)recievbuf) )
            {
                FTPClientGetFile(_socketID,recievbuf);
                break;
            }
            else
            {
                ln = FTPClientGetCommand(recievbuf,INOUT command, INOUT param);
                fprintf(stdout,"%s %s",command,param);
                if (((FTPHead*)recievbuf)->u_FTPbitfild.FTPUnionHeader.m_islast == LAST_P)
                {
                    break;
                }
            }
        }

        *command=0;
        *param = 0;
    }


    return 0;
}

static void FTPclientConvertHeader(FTPHead* _recievbuf)
{
    _recievbuf->u_FTPbitfild.m_headerAsUnit =\
  /*  ntohl*/(_recievbuf->u_FTPbitfild.m_headerAsUnit);
    _recievbuf->m_position  =/* ntohl*/(_recievbuf->m_position);
    return;
}

#ifdef unitest_ftp_client
long int seekpossition =0;
#endif

int FTPGlientGetUserPromt(char*  command,char*   param)
{
#ifdef unitest_ftp_client
    FILE*       fd;
    int         pipesfd[2];
    char        buf[1000];/*used to get user promt from file instead of STDIN*/
    int         help;
    int         helpin;
#endif /*unitest_ftp_client*/

    char            promt[10*MAXPROMTLENGH]={0};/*The actual promt size will be checked later*/
    puts("\nPut command$:\n");


#ifdef unitest_ftp_client
    pipe(pipesfd);
    fd =fopen("output","r");
    fseek (fd,seekpossition,SEEK_SET);
    fgets(buf,1000,fd);
    if(feof(fd))
    {
        seekpossition =0;
        fclose(fd);
        exit(0);
    }
    seekpossition =ftell(fd);
    help = dup(STDOUT_FILENO);/*Storing the stdout in temp variables */
    helpin = dup(STDIN_FILENO);
    dup2(pipesfd[1],STDOUT_FILENO);/*All STDOUT will redirected to pipe*/
    dup2(pipesfd[0],STDIN_FILENO);
    fprintf(stdout,"%s",buf);
#endif
    gets(promt);

#ifdef unitest_ftp_client
    close(pipesfd[0]);
    dup2(helpin,STDIN_FILENO);
    dup2(help,STDOUT_FILENO);/* Returns the stdout to screen*/
    close(pipesfd[1]);
    fclose(fd);
#endif
    return FTPCommandParser(promt,OUT command,OUT param);
}


int FTPCommandParser(void* _buffer,OUT char* _command,OUT char* _param)
{
    char*       com = _buffer;

    assert(_buffer);
    if ( strlen(_buffer) > MAXPROMTLENGH)
    {
#ifdef unitest_ftp_client
fprintf(stdout,"Input was too long \n");
#endif /*    unitest_ftp_client*/
        return 1;
    }
    *_command=0;
    *_param  = 0;

     com=strtok(_buffer," ");
     if(com == 0) /*Nothing promt*/
     {

#ifdef unitest_ftp_client
fprintf(stdout,"Input was empty buffer\n");
#endif /*    unitest_ftp_client*/
         return 1;
     }

#ifdef unitest_ftp_client
fprintf(stdout,"Input was :%s %s\n",(char*)_buffer,(char*)_buffer+strlen((char*)_buffer)+1);

#endif /*    unitest_ftp_clie socket - create an endpoint for communicationnt*/


     strcpy(_command,com);
     com = strtok(NULL," ");

     if( com != 0 )
     {
        while(com)
        {
            strcat(_param,com);
            com=strtok(NULL," ");
            if(com!=0)
            {
                strcat(_param," ");
            }
        }
     }
     else
     {
         *_param=0;
     }
     return 0;
}

int FTPFinedInListOfStr(char* _str, char* _liststr,int _sizeoflist, int _lenghtstr)
{
    int         i;
    assert(_str);
    assert(_liststr);

    for(i=0; i < _sizeoflist; ++i)
    {
        if ( ! strcmp(_str,_liststr+ i*_lenghtstr))
        {
            return i;
        }
    }
    puts("Wrong Input\n");
    return -1;
}

void FTPFillHeadr(void* _headbuf,size_t _position, uint _islast, uint _error, uint _iscommand)

{
    FTPHead*    hd = (FTPHead*)_headbuf;

    hd->m_position =/*htonl*/( _position);/*start position of package body*/

    hd->u_FTPbitfild.FTPUnionHeader.m_islast    =  _islast;/*is last*/
    hd->u_FTPbitfild.FTPUnionHeader.m_errnum    =  _error;/*error log*/
    hd->u_FTPbitfild.FTPUnionHeader.m_iscommand = _iscommand;
    hd->u_FTPbitfild.FTPUnionHeader.m_reserved  = 0;
    return;
}

int FTPClientAddHeader(char* _headrbuf, char* _command,char* _param)
{
    size_t       l = sizeof(FTPHead);
    char*        start=_headrbuf+l;
    size_t       datalength;

    strcpy(start,_command);
    strcat(start," ");
    strcat(start,_param);

    datalength = strlen(start)+1;
    ((FTPHead*)_headrbuf)->u_FTPbitfild.FTPUnionHeader.m_datasize = datalength ;

    ((FTPHead*)_headrbuf)->u_FTPbitfild.m_headerAsUnit = /*htonl*/(((FTPHead*)_headrbuf)->u_FTPbitfild.m_headerAsUnit);

    return datalength  + l;
}




static int FTPClientIsFile(void* _recievbuf)
{
    assert(_recievbuf);
   if ( ((FTPHead*)_recievbuf)->u_FTPbitfild.FTPUnionHeader.m_iscommand == COMMAND_C )
   {
       return 0;
   }

   if( ((FTPHead*) _recievbuf)->u_FTPbitfild.FTPUnionHeader.m_iscommand == FILE_C )
   {
       return 1;
   }
   return -1;
}

int FTPClientGetCommand(char* _recievbuf,INOUT char* _command, INOUT char* _param)
{
    if(((FTPHead*)_recievbuf)->u_FTPbitfild.FTPUnionHeader.m_errnum == 255)
    {
        strcpy(_command,_recievbuf+sizeof(FTPHead));
         *_param=0;
        return 0;

    }
    return FTPCommandParser(_recievbuf+sizeof(FTPHead),OUT _command,OUT _param);
}

static int FTPClientGetFile(int _socketID,void* _recievbuf)
{
    FTPHead         head=HeadNULL;
    FILE*           fp;
    int             totalLength = -1;
    int             readedLength = 0;
    size_t          wr;
    size_t          iscorrupted = 0;
    size_t          count = 0 ;

    assert(_recievbuf);

    /*HERE I DON'T FTPReadHeadr use the funciton because i already did stohl*/
    head.u_FTPbitfild.FTPUnionHeader.m_iscommand = ((FTPHead*)_recievbuf)->u_FTPbitfild.FTPUnionHeader.m_iscommand;
    head.u_FTPbitfild.FTPUnionHeader.m_datasize  = ((FTPHead*)_recievbuf)->u_FTPbitfild.FTPUnionHeader.m_datasize;
    head.u_FTPbitfild.FTPUnionHeader.m_errnum    = ((FTPHead*)_recievbuf)->u_FTPbitfild.FTPUnionHeader.m_errnum;
    head.u_FTPbitfild.FTPUnionHeader.m_islast    = ((FTPHead*)_recievbuf)->u_FTPbitfild.FTPUnionHeader.m_islast;
    head.u_FTPbitfild.FTPUnionHeader.m_reserved  = 0;
    head.m_position  = ((FTPHead*)_recievbuf)->m_position;


/*TODO*/fp = FTPClientOpenNewFile();/*NEED TO WRITE case when file not opened*/

    if(!fp)
    {
        fprintf(stderr,"\n Failed to open file \n");
        exit(EXIT_FAILURE);
        while( (recv(_socketID,_recievbuf,MAXPACKAGESIZE,0)) )/*Drops all recieved data*/
        {
            sleep(0.2);
        }


        return 1;
    }

    while(1)
    {


        fseek(fp,head.m_position,SEEK_SET);
        wr = fwrite( (char*)_recievbuf+sizeof(FTPHead),1,head.u_FTPbitfild.FTPUnionHeader.m_datasize ,fp);
        readedLength+=wr;
        if( wr != head.u_FTPbitfild.FTPUnionHeader.m_datasize)
        {
            iscorrupted =1;
        }

        if( head.u_FTPbitfild.FTPUnionHeader.m_islast == LAST_P)
        {
            totalLength = head.m_position + head.u_FTPbitfild.FTPUnionHeader.m_datasize;
        }

        if(readedLength == totalLength)
        {
            fclose(fp);
            break;
        }


        usleep(1);/*To gives some time to the next packet recieve the  socket*/
        wr = recv(_socketID,_recievbuf,MAXPACKAGESIZE,0);/*will be  blocked untill message not recieved*/
        if(wr ==-1)
        {
            perror("recv :");
            fprintf(stderr,"Problems with recieving of filedata line :%d, file :%s\n",__LINE__,__FILE__);
            fclose(fp);
            return  1;
        }
       ++count;
        FTPReadHeadr(_recievbuf,&head);
        if (head.u_FTPbitfild.FTPUnionHeader.m_iscommand == COMMAND_C)
        {
            fprintf(stderr,"\n Wrong packet recieved\n");
            fclose(fp);
            exit(EXIT_FAILURE);
            return 1;
        }
    }

    if(iscorrupted)
    {
        fprintf(stderr,"\n recieved file is partially corrupted\n");
    }
    return 0;
}


void FTPReadHeadr(void* _buff,OUT FTPHead* _header)
{

     ((FTPHead*)_buff)->u_FTPbitfild.m_headerAsUnit =\
    /* ntohl*/(((FTPHead*)_buff)->u_FTPbitfild.m_headerAsUnit);

    _header->u_FTPbitfild.FTPUnionHeader.m_iscommand     = ((FTPHead*)_buff)->u_FTPbitfild.FTPUnionHeader.m_iscommand;
    _header->u_FTPbitfild.FTPUnionHeader.m_datasize      = ((FTPHead*)_buff)->u_FTPbitfild.FTPUnionHeader.m_datasize;
    _header->u_FTPbitfild.FTPUnionHeader.m_errnum        = ((FTPHead*)_buff)->u_FTPbitfild.FTPUnionHeader.m_errnum;
    _header->u_FTPbitfild.FTPUnionHeader.m_islast        = ((FTPHead*)_buff)->u_FTPbitfild.FTPUnionHeader.m_islast;
    _header->u_FTPbitfild.FTPUnionHeader.m_reserved      = 0;

    _header->m_position  =/* ntohl*/(((FTPHead*)_buff)->m_position);

    return;
}



static FILE* FTPClientOpenNewFile(void)
{

    char        filename[MAXPROMTLENGH];

    puts("File name :");
    gets(filename);

    return fopen(filename,"w");
}


